package com.nakamachizu.service;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.logging.Logger;

import org.apache.commons.lang.RandomStringUtils;
import org.slim3.datastore.Datastore;
import org.slim3.util.StringUtil;
import org.slim3.util.TimeZoneLocator;

import com.google.appengine.api.datastore.GeoPt;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.datastore.PreparedQuery.TooManyResultsException;
import com.nakamachizu.collection.IconNameMap;
import com.nakamachizu.collection.ZoomRangeMap;
import com.nakamachizu.constant.AppConstant;
import com.nakamachizu.constant.MixiConstant;
import com.nakamachizu.meta.MapDataMeta;
import com.nakamachizu.meta.session.MixiSessionDataMeta;
import com.nakamachizu.model.LandMarkData;
import com.nakamachizu.model.LineData;
import com.nakamachizu.model.MapData;
import com.nakamachizu.model.MemberData;
import com.nakamachizu.model.session.MapDataSession;
import com.nakamachizu.model.session.MixiSessionData;
import com.nakamachizu.util.ErrorsUtil;


public class MapDataService {

    private static final Logger log = Logger.getLogger(MapDataService.class.getName());

    
    public String makeAccessKey() throws Exception{
        
        MapDataMeta meta = MapDataMeta.get();
        String accessKey = RandomStringUtils.randomAlphanumeric(24);
        int i=0;
        while(Datastore.query(meta).filter(meta.accessKey.equal(accessKey)).countQuickly() > 0){
            accessKey = RandomStringUtils.randomAlphanumeric(24);
            i++;
            if(i>100){
                throw new Exception("generate access key failed.");
            }
        }
        return accessKey;
    }
    
    public MapData getMapData(String adminMailAddress){

        //System.out.println(adminMailAddress);
        Calendar now = Calendar.getInstance(TimeZoneLocator.get());
        MapDataMeta mm = MapDataMeta.get();
        List<MapData> list = Datastore.query(mm).filter(mm.adminMailAddress.equal(adminMailAddress), mm.expire.greaterThan(now.getTime())).asList();
        if(list != null && list.size() == 1){
            return list.get(0);
        }else{
            return null;
        }
    }
    
    public MapData getMapDataAccessKey(String accessKey){

        Calendar now = Calendar.getInstance(TimeZoneLocator.get());
        try{
            MapDataMeta meta = MapDataMeta.get();
            MapData map = Datastore.query(meta).filter(meta.accessKey.equal(accessKey)).asSingle();
            if(map != null){
                Calendar cal = Calendar.getInstance(TimeZoneLocator.get());
                cal.setTime(map.getExpire());
                if(cal.after(now)){
                    return map;
                }
            }
        }catch(TooManyResultsException e){
            log.severe(ErrorsUtil.stackTrace(e));
        }
        return null;
    }
    
    public MapData getMapDataMixi(MixiSessionData session){
        if(session != null && session.isOwner()){
            return this.getMapDataAccessKey(session.getMapKey());
        }
        
        return null;
    }
    
    public MapData getMapDataMixiViewer(MixiSessionData session){
        MapData result = null;
        if(session != null){
            result = this.getMapDataAccessKey(session.getMapKey());
        }
        
        return result;
    }
    
    public MixiSessionData getMixiSessionData(String mixiSessionId) throws TooManyResultsException {
        if(!StringUtil.isEmpty(mixiSessionId)){
            MixiSessionDataMeta meta = MixiSessionDataMeta.get();
            MixiSessionData msd = Datastore.query(meta).filter(meta.accessKey.equal(mixiSessionId)).asSingle();
            if(msd != null){
                Calendar cal = Calendar.getInstance(TimeZoneLocator.get());
                cal.add(Calendar.MINUTE, MixiConstant.MIXI_SESSION_MINUTES());
                msd.setExpireDate(cal.getTime());
                Datastore.put(msd);
                return msd;
            }
        }
        return null;
    }
    
    public List<LandMarkData> allocateLandMark(MapData map){
        
        List<LandMarkData> list = map.getLandMarkDataListRef().getModelList();
        if(list != null){
            int i = 0;
            for(LandMarkData landmark : list){
                landmark.setMIconName(AppConstant.MOBILE_LANDMARK_ICON_NAME_PREFIX() + Integer.toString(i));
                i++;
            }
        }
        
        return list;
    }
    
    public List<LandMarkData> allocateLandMark(List<LandMarkData> list){
        if(list != null){
            int i = 0;
            for(LandMarkData landmark : list){
                landmark.setMIconName(AppConstant.MOBILE_LANDMARK_ICON_NAME_PREFIX() + Integer.toString(i));
                i++;
            }
        }
        return list;
    }
    
    public String createMobileLandmarkIconName(List<LandMarkData> list){
        int no = 0;
        if(list != null){
            no = list.size();
        }
        return AppConstant.MOBILE_LANDMARK_ICON_NAME_PREFIX() + Integer.toString(no);
    }
    
    public List<MemberData> allocateMemberData(List<MemberData> list){
        if(list != null){
            int i = 0;
            for(MemberData mem : list){
                mem.setPcIconName(AppConstant.PC_ICON_NAME_PREFIX() + Integer.toString(i));
                mem.setMIconName(AppConstant.MOBILE_MEMBER_ICON_NAME_PREFIX() + IconNameMap.MOBILE_MEMBER_ICON_NAME.get(Integer.toString(i)));
                i++;
            }
        }
        return list;
    }
    
    public String createMemberPcIconName(List<MemberData> list){
        int no = 0;
        if(list != null){
            no = list.size();
        }
        return AppConstant.PC_ICON_NAME_PREFIX() + Integer.toString(no);
    }
    
    public String createMobileMemberIconName(List<MemberData> list){
        int no = 0;
        if(list != null){
            no = list.size();
        }
        return AppConstant.MOBILE_MEMBER_ICON_NAME_PREFIX() + IconNameMap.MOBILE_MEMBER_ICON_NAME.get(Integer.toString(no));
    }
    
    public void deleteMapData(MapData map){
        
        List<MemberData> mList = map.getMemberDataListRef().getModelList();
        List<LandMarkData> lList = map.getLandMarkDataListRef().getModelList();
        List<LineData>lines = map.getLineDataListRef().getModelList();
        
        if(mList != null){
            for(MemberData m : mList){
                Datastore.delete(m.getKey());
            }
        }
        
        if(lList != null){
            for(LandMarkData l : lList){
                Datastore.delete(l.getKey());
            }
        }
        
        if(lines != null){
            for(LineData line : lines){
                Datastore.delete(line.getKey());
            }
        }
        
        Datastore.delete(map.getKey());
    }
    
    public MapDataSession createMapDataSession(MapData map){
        MapDataSession mds = null;
        if(map != null){
            mds = new MapDataSession();
            mds.setMapKey(map.getAccessKey());
            //mds.setViewPassword(map.getViewPassword());
        }
        return mds;
    }
    
    public MapData getMapDataFromSession(MapDataSession mdses){
        MapData result = null;
        if(mdses != null){
            MapData tmp = this.getMapDataAccessKey(mdses.getMapKey());
            if(tmp != null){
                if(StringUtil.isEmpty(tmp.getViewPassword()) || tmp.getViewPassword().equals(mdses.getViewPassword())){
                    result = tmp;
                }
            }
        }
        return result;
    }
    
    
    public MemberData getMemberDataUid(List<MemberData>list , String uid){
        if(list != null && !StringUtil.isEmpty(uid)){
            for(MemberData data : list){
                if(uid.equals(data.getUid())){
                    return data;
                }
            }
        }
        return null;
    }
    
    public GeoPt getCenter(String centerLat, String centerLng, float r, float l, float u, float d){
        double lat = Double.parseDouble(centerLat);
        double lng = Double.parseDouble(centerLng);
        lat = lat + u - d;
        lng = lng + r - l;
        if(lat > 90){
            lat = lat - 180;
        }
        if(lat < -90){
            lat = lat + 180;
        }
        if(lng > 180){
            lng = lng - 360;
        }
        if(lng < -180){
            lng = lng + 360;
        }
        DecimalFormat fmt = new DecimalFormat("###.000000");
        String latStr = fmt.format(lat);
        String lngStr = fmt.format(lng);
        return new GeoPt(Float.parseFloat(latStr), Float.parseFloat(lngStr));
    }
    
    public float getAngle(String zoomLevel, int width){
        Float angle = ZoomRangeMap.ZOOM_RANGE_MAP.get(zoomLevel);
        float n = 1.0f;
        if(angle == null){
            angle = 0f;
        }
        if(width > 300){
            n = 2.0f;
        }else if(width > 600){
            n = 3.0f;
        }
        return angle.floatValue() * n;
    }
    
    public Date getExpire(int days){
        Calendar cal = Calendar.getInstance(TimeZoneLocator.get());
        cal.add(Calendar.DAY_OF_MONTH, days);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        cal.set(Calendar.MILLISECOND, 0);
        
        return cal.getTime();
    }
    
    public String getLineColor(MapData map, String guid){
        List<LineData>lline = map.getLineDataListRef().getModelList();
        if(lline != null){
            for(LineData line : lline){
                if(guid.equals(line.getGuid())){
                    return line.getColor();
                }
            }
        }
        return null;
    }
    
    public List<Key> getDeleteLineData(MapData map, String guid){
        List<Key>result = null;
        List<LineData>lline = map.getLineDataListRef().getModelList();
        if(lline != null && lline.size() > 0){
            result = new ArrayList<Key>();
            for(LineData line : lline){
                if(guid.equals(line.getGuid())){
                    result.add(line.getKey());
                }
            }
        }
        return result;
    }
    
    /**
    @Deprecated
    public MapPosition getAverageCenter(List<LandMarkData>lList, List<MemberData> mList){
        MapPosition result = null;
        double lat = 0;
        double lng = 0;
        double size = 0;
        double maxLat = -90.000000;
        double minLat = 90.999999;
        double maxLng = -180.000000;
        double minLng = 180.999999;
        if(lList != null){
            size = (double)lList.size();
            for(LandMarkData data : lList){
                if(maxLat < data.getLatLng().getLatitude()){
                    maxLat = data.getLatLng().getLatitude();
                }
                if(maxLng < data.getLatLng().getLongitude()){
                    maxLng = data.getLatLng().getLongitude();
                }
                if(minLat > data.getLatLng().getLatitude()){
                    minLat = data.getLatLng().getLatitude();
                }
                if(minLng > data.getLatLng().getLongitude()){
                    minLng = data.getLatLng().getLongitude();
                }
                lat += data.getLatLng().getLatitude();
                lng += data.getLatLng().getLongitude();
            }
        }
        if(mList != null){
            size = size + (double)mList.size();
            for(MemberData data : mList){
                if(maxLat < data.getLatLng().getLatitude()){
                    maxLat = data.getLatLng().getLatitude();
                }
                if(maxLng < data.getLatLng().getLongitude()){
                    maxLng = data.getLatLng().getLongitude();
                }
                if(minLat > data.getLatLng().getLatitude()){
                    minLat = data.getLatLng().getLatitude();
                }
                if(minLng > data.getLatLng().getLongitude()){
                    minLng = data.getLatLng().getLongitude();
                }
                lat += data.getLatLng().getLatitude();
                lng += data.getLatLng().getLongitude();
            }
        }
        lat = lat / size;
        lng = lng / size;
        DecimalFormat fmt = new DecimalFormat("###.000000");
        
        // zoomレベルを決定する
        double diffLat = maxLat - minLat;
        double difLat = maxLng - minLng;
        
        
        return result;
    }
    */
}
